home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume14 / mush6.0 / part10 < prev    next >
Encoding:
Internet Message Format  |  1988-04-12  |  32.3 KB

  1. From: island!argv@sun.com (Dan Heller)
  2. Subject: Mail User's Shell, version 6.0
  3.  
  4. #! /bin/sh
  5. # This is a shell archive.  Remove anything before this line, then unpack
  6. # it by saving it into a file and typing "sh file".  To overwrite existing
  7. # files, type "sh file -c".  You can also feed this as standard input via
  8. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  9. # will see the following message at the end:
  10. #        "End of archive 10 (of 14)."
  11. # Contents:  hdrs.c
  12. # Wrapped by rsalz@fig.bbn.com on Wed Apr 13 20:04:53 1988
  13. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  14. if test -f 'hdrs.c' -a "${1}" != "-c" ; then 
  15.   echo shar: Will not clobber existing file \"'hdrs.c'\"
  16. else
  17. echo shar: Extracting \"'hdrs.c'\" \(30675 characters\)
  18. sed "s/^X//" >'hdrs.c' <<'END_OF_FILE'
  19. X/* hdrs.c     (c) copyright 1986 (Dan Heller) */
  20. X
  21. X/* Routines that deal with message headers inside messages */
  22. X#include "mush.h"
  23. X
  24. X/*
  25. X * get which message via the offset and search for the headers which
  26. X * match the string "str". there may be more than one of a field (like Cc:)
  27. X * so get them all and "cat" them together into the static buffer
  28. X * "buf" and return its address.
  29. X */
  30. char *
  31. header_field(n, str)
  32. char *str;
  33. X{
  34. X    static char    buf[BUFSIZ];
  35. X    char        tmp[BUFSIZ];
  36. X    register char  *p, *p2, *b = buf;
  37. X    int contd_hdr;  /* true if next line is a continuation of the hdr we want */
  38. X
  39. X    if (fseek(tmpf, msg[n].m_offset, L_SET) == -1) {
  40. X    error("fseek in %s (msg %d, folder=%s)", tempfile, n+1, mailfile);
  41. X    turnon(glob_flags, READ_ONLY);
  42. X    return NULL;
  43. X    }
  44. X    *b = 0;
  45. X    while((p = fgets(tmp, sizeof(buf), tmpf)) && *p != '\n') {
  46. X    if (*p != ' ' && *p != '\t') {
  47. X        contd_hdr = 0;
  48. X        /* strcmp ignoring case */
  49. X        for(p2 = str; *p && *p2 && *p2 == lower(*p); ++p, ++p2);
  50. X        /* MATCH is true if p2 is at the end of str and *p is ':' */
  51. X        if (*p2 || *p++ != ':')
  52. X        continue;
  53. X        else
  54. X        contd_hdr = 1;
  55. X    } else if (!contd_hdr)
  56. X        continue;
  57. X    skipspaces(0);
  58. X    p2 = no_newln(p);
  59. X    *++p2 = ' ', *++p2 = 0;
  60. X    if (strlen(p) + (b-buf) < sizeof (buf))
  61. X        b += Strcpy(b, p);
  62. X    }
  63. X    if (b > buf) /* now get rid of the trailing blank */
  64. X    *--b = 0;
  65. X    return (*buf)? buf: NULL;
  66. X}
  67. X
  68. do_hdrs(argc, argv, list)
  69. register char **argv, list[];
  70. X{
  71. X    register int   pageful = 0, fnd;
  72. X    int        (*oldint)(), (*oldquit)(), show_deleted;
  73. X    static int     cnt;
  74. X    register char  *p;
  75. X    char        first_char = (argc) ? **argv: 'h';
  76. X
  77. X    if (argc > 1 && !strcmp(argv[1], "-?"))
  78. X    return help(0, "headers", cmd_help);
  79. X
  80. X    if (!msg_cnt) {
  81. X    if (ison(glob_flags, DO_PIPE))
  82. X        return 0;
  83. X#ifdef CURSES
  84. X    if (iscurses)
  85. X        clear();
  86. X#endif /* CURSES */
  87. X#ifdef SUNTOOL
  88. X    if (istool)
  89. X        mail_status(0);
  90. X#endif /* SUNTOOL */
  91. X    return 0;
  92. X    }
  93. X    if (first_char == ':' || (argc > 1 && argv[1][0] == ':')) {
  94. X    if (first_char != ':')
  95. X        argv++;
  96. X    return specl_hdrs(argv, list);
  97. X    }
  98. X
  99. X    on_intr();
  100. X
  101. X    if (argc && (argv[0][1] == '+' || argc > 1 && !strcmp(argv[1], "+")) ||
  102. X        first_char == 'z' && !argv[1])
  103. X    if (msg_cnt > screen)
  104. X        cnt = min(msg_cnt - screen, n_array[0] + screen);
  105. X    else
  106. X        cnt = 0;
  107. X    else if (argc && (argv[0][1] == '-' || argc > 1 && !strcmp(argv[1], "-")))
  108. X    cnt = max((cnt - 2*screen), 0);
  109. X    else if (argc && *++argv &&
  110. X    (isdigit(**argv) || **argv == '^' || **argv == '$' || **argv == '.')
  111. X     || ison(glob_flags, IS_PIPE)) {
  112. X    /* if we're coming from a pipe, start display at the first msg bit
  113. X     * set in the msg_list
  114. X     */
  115. X    if (ison(glob_flags, IS_PIPE)) {
  116. X        if (isoff(glob_flags, DO_PIPE))
  117. X        for (fnd = 0; fnd < msg_cnt; fnd++)
  118. X            if (msg_bit(list, fnd))
  119. X            wprint("%s\n", compose_hdr(fnd));
  120. X        off_intr();
  121. X        return 0;
  122. X    }
  123. X    /* if a number was given, use it */
  124. X    if (!(fnd = chk_msg(*argv))) {
  125. X        off_intr();
  126. X        return -1;
  127. X    }
  128. X    for (cnt = fnd - 1; cnt > 0 && cnt + screen > msg_cnt; cnt--);
  129. X    } else if (current_msg < n_array[0] || current_msg > n_array[screen-1])
  130. X    cnt = current_msg; /* adjust if user reads passed screen bounds */
  131. X    else if (cnt >= msg_cnt || !argc || !*argv)
  132. X    cnt = max((cnt - screen), 0); /* adjust window to maintian position */
  133. X
  134. X    show_deleted = !!do_set(set_options, "show_deleted");
  135. X
  136. X    for (;pageful<screen && cnt<msg_cnt && isoff(glob_flags, WAS_INTR); cnt++) {
  137. X    if (!iscurses && !show_deleted && first_char == 'h'
  138. X        && ison(msg[cnt].m_flags, DELETE))
  139. X        continue;
  140. X    n_array[pageful++] = cnt;
  141. X    /* this message was displayed -- set the bit */
  142. X    if (list)
  143. X        set_msg_bit(list, cnt);
  144. X    /* if do_pipe, don't output anything */
  145. X    if (ison(glob_flags, DO_PIPE))
  146. X        continue;
  147. X    p = compose_hdr(cnt);
  148. X    if (!istool && (!iscurses || ison(glob_flags, IS_GETTING)))
  149. X        puts(p);
  150. X#ifdef SUNTOOL
  151. X    else if (istool) {
  152. X        if (cnt == current_msg) /* embolden or reverse_video */
  153. X        highlight(hdr_win, 0,pageful*l_height(DEFAULT), DEFAULT,p);
  154. X        else
  155. X        pw_text(hdr_win, 0, pageful * l_height(DEFAULT), PIX_SRC,
  156. X                            fonts[DEFAULT], p);
  157. X        Clrtoeol(hdr_win, strlen(p)*l_width(DEFAULT),
  158. X             pageful*l_height(DEFAULT), DEFAULT);
  159. X    }
  160. X#endif /* SUNTOOL */
  161. X#ifdef CURSES
  162. X        else if (iscurses)
  163. X        mvprintw(pageful, 0, "%-.*s", COLS-2, p), clrtoeol();
  164. X#endif /* CURSES */
  165. X    }
  166. X    /* just in case a signal stopped us */
  167. X    off_intr();
  168. X    pageful++;
  169. X#ifdef CURSES
  170. X    if (iscurses && pageful < screen)
  171. X    move(pageful, 0), clrtobot();
  172. X#endif /* CURSES */
  173. X    if (cnt == msg_cnt) {
  174. X    while (pageful <= screen) {
  175. X        n_array[pageful-1] = msg_cnt+1; /* assign out-of-range values */
  176. X#ifdef SUNTOOL
  177. X        if (istool)
  178. X        Clrtoeol(hdr_win, 0, pageful * l_height(DEFAULT), DEFAULT);
  179. X#endif /* SUNTOOL */
  180. X        ++pageful;
  181. X    }
  182. X    }
  183. X#ifdef SUNTOOL
  184. X    if (istool) {
  185. X    if (msg_cnt > screen) {
  186. X        panel_set(next_scr, PANEL_SHOW_ITEM, TRUE, 0);
  187. X        panel_set(prev_scr, PANEL_SHOW_ITEM, TRUE, 0);
  188. X    }
  189. X    mail_status(0);
  190. X    }
  191. X#endif /* SUNTOOL */
  192. X    return 0;
  193. X}
  194. X
  195. X#define NEW 1
  196. X#define ALL 2
  197. X
  198. specl_hdrs(argv, list)
  199. char **argv, list[];
  200. X{
  201. X    u_long    special = 0;
  202. X    int     n = 0;
  203. X
  204. X    while (argv[0][++n])
  205. X    switch(argv[0][n]) {
  206. X        case 'a': special = ALL;
  207. X        when 'n': special = NEW;
  208. X        when 'u': special = UNREAD;
  209. X        when 'o': special = OLD;
  210. X        when 'd': special = DELETE;
  211. X        when 'r': special = REPLIED;
  212. X        otherwise: print("choose from n,u,o,d,r, or a"); return -1;
  213. X    }
  214. X    if (debug)
  215. X    (void) check_flags(special);
  216. X
  217. X    for (n = 0; n < msg_cnt; n++) {
  218. X    /*
  219. X     * First, see if we're looking for NEW messages.
  220. X     * If so, then check to see if the msg is unread and not old.
  221. X     * If special > ALL, then special has a mask of bits describing
  222. X     * the state of the message.
  223. X     */
  224. X    if (ison(glob_flags, IS_PIPE)&& !msg_bit(list, n))
  225. X        continue;
  226. X    if (special == ALL || special == NEW &&
  227. X           (ison(msg[n].m_flags, UNREAD) && isoff(msg[n].m_flags, OLD))) {
  228. X        if (isoff(glob_flags, DO_PIPE))
  229. X        print("%s\n", compose_hdr(n));
  230. X        if (list)
  231. X        set_msg_bit(list, n);
  232. X#ifndef SYSV
  233. X    /*
  234. X     * XENIX compiler can't handle "special" in ison() macro.
  235. X     * It only works if the second argument is a constant!
  236. X     */
  237. X    } else if (special > ALL && ison(msg[n].m_flags, special)) {
  238. X        if (isoff(glob_flags, DO_PIPE))
  239. X        print("%s\n", compose_hdr(n));
  240. X        if (list)
  241. X        set_msg_bit(list, n);
  242. X#endif /* SYSV */
  243. X    } else {
  244. X        if (list)
  245. X        unset_msg_bit(list, n);
  246. X        if (debug) {
  247. X        printf("msg[%d].m_flags: %d", n, msg[n].m_flags);
  248. X        (void) check_flags(msg[n].m_flags);
  249. X        }
  250. X    }
  251. X    }
  252. X    return 0;
  253. X}
  254. X
  255. X#define Strncpy(buf,p) (void) strncpy(buf,p, sizeof(buf)),buf[sizeof(buf)-1]=0
  256. X
  257. X/*
  258. X * compose a header from the information about a message (from, to, date,
  259. X * subject, etc..).  The header for message number "cnt" is built and is
  260. X * returned in the static buffer "buf".  There will be *at least* 9 chars
  261. X * in the buffer which will be something like: " 123 >N " The breakdown
  262. X * is as follows: 4 chars for the message number, 1 space, 1 char for '>'
  263. X * (if current message) and two spaces for message status (new, unread, etc)
  264. X * followed by 1 terminating space.
  265. X * Read other comments in the routine for more info.
  266. X */
  267. char *
  268. compose_hdr(cnt)
  269. X{
  270. X    static char buf[256];
  271. X    register char *p, *b;
  272. X    char from[256], subject[256], date[17], lines[16], chars[16], line[256];
  273. X    char to[256], addr[256], name[256], status[2];
  274. X    char Day[3], Mon[4], Tm[8], Yr[5], Wkday[4];
  275. X
  276. X    /* status of the message */
  277. X    if (ison(msg[cnt].m_flags, DELETE))
  278. X    status[0] = '*';
  279. X    else if (ison(msg[cnt].m_flags, OLD) && ison(msg[cnt].m_flags, UNREAD))
  280. X    status[0] = 'U';
  281. X    else if (ison(msg[cnt].m_flags, PRESERVE))
  282. X    status[0] = 'P';
  283. X    else if (isoff(msg[cnt].m_flags, UNREAD))
  284. X    status[0] = ' ';
  285. X    else
  286. X    status[0] = 'N';
  287. X
  288. X    if (ison(msg[cnt].m_flags, REPLIED))
  289. X    status[1] = 'r';
  290. X    else
  291. X    status[1] = ' ';
  292. X
  293. X    to[0] = from[0] = subject[0] = date[0] = lines[0] = chars[0] = addr[0] =
  294. X    name[0] = line[0] = Day[0] = Mon[0] = Tm[0] = Yr[0] = Wkday[0] = 0;
  295. X
  296. X    /* who's the message to */
  297. X    if ((p = header_field(cnt, "to")) ||
  298. X    (p = header_field(cnt, "resent-to")) ||
  299. X    (p = header_field(cnt, "apparently-to")))
  300. X    Strncpy(to, p);
  301. X
  302. X    /* who the messages is from--
  303. X     * %f        From field
  304. X     * %a        From address
  305. X     * %n        From name
  306. X     */
  307. X    if (!(p = header_field(cnt, "from"))) {
  308. X    /* if all else fails, then get the first token in "From" line */
  309. X    register char *p2;
  310. X    p = ""; /* just in case */
  311. X    if (fseek(tmpf, msg[cnt].m_offset, L_SET) == -1 ||
  312. X        !(p2 = fgets(line, sizeof(line), tmpf))) {
  313. X        error("fseek in %s (msg %d, folder=%s)", tempfile, cnt+1, mailfile);
  314. X        turnon(glob_flags, READ_ONLY);
  315. X    } else if (!(p = index(p2, ' ')))
  316. X        print("Fudged \"From\" line: %s", p2);
  317. X    else if (p2 = any(++p, " \t"))
  318. X        *p2 = 0;
  319. X    }
  320. X    skipspaces(0);
  321. X    (void) no_newln(p);
  322. X    /* if the "from" line produced the user's login name, then the message is
  323. X     * from the user -- attempt to give more useful information by telling
  324. X     * to whom the message was sent.  This is not possible if the "to" header
  325. X     * failed to get info (which is probably impossible).
  326. X     */
  327. X    if (!strcmp(p, login) && *to) {
  328. X    (void) strcpy(from, "TO: ");
  329. X    (void) strncpy(from+4, to, sizeof(from)-4), from[sizeof(from)-4] = 0;
  330. X    (void) get_name_n_addr(from+4, name+4, addr+4);
  331. X    if (name[4])
  332. X        (void) strncpy(name,"TO: ",4); /* strncpy doesn't null terminate */
  333. X    if (addr[4])
  334. X        (void) strncpy(addr,"TO: ",4); /* don't overwrite name there */
  335. X    } else {
  336. X    Strncpy(from, p);
  337. X    (void) get_name_n_addr(from, name, addr);
  338. X    }
  339. X
  340. X    if (p = msg_date(cnt))
  341. X    date_to_string(p, Yr, Mon, Day, Wkday, Tm, date);
  342. X
  343. X    (void) sprintf(lines, "%d", msg[cnt].m_lines);
  344. X    (void) sprintf(chars, "%ld", msg[cnt].m_size);
  345. X
  346. X    /* and the subject */
  347. X    if (p = header_field(cnt, "subject"))
  348. X    Strncpy(subject, p);
  349. X
  350. X    /* now, construct a header out of a format string */
  351. X    if (!hdr_format)
  352. X    hdr_format = DEF_HDR_FMT;
  353. X
  354. X    (void) sprintf(buf, "%4.d ", cnt+1);
  355. X    b = buf+5;
  356. X    *b++ = ((cnt == current_msg && !iscurses)? '>': ' ');
  357. X    *b++ = status[0], *b++ = status[1];
  358. X    *b++ = ' ';
  359. X    /* use the cnt variable to count chars since beginning of buf
  360. X     * initialize to 9 (strlen(buf) so far).  This magic number is
  361. X     * used in other places in msgs.c and mail.c
  362. X     */
  363. X    cnt = 9;
  364. X    for (p = hdr_format; *p; p++)
  365. X    if (*p == '\\')
  366. X        switch (*++p) {
  367. X        case 't':
  368. X            while (cnt % 8)
  369. X            cnt++, *b++ = ' ';
  370. X        when 'n':
  371. X            cnt = 1, *b++ = '\n';
  372. X        otherwise: cnt++, *b++ = *p;
  373. X        }
  374. X    else if (*p == '%') {
  375. X        char fmt[64];
  376. X        register char *p2 = fmt;
  377. X        int len, got_dot = FALSE;
  378. X
  379. X        *p2++ = '%';
  380. X        if (p[1] != '-')
  381. X        *p2++ = '-';
  382. X        else
  383. X        *++p;
  384. X        while (isdigit(*++p) || !got_dot && *p == '.') {
  385. X        if (*p == '.')
  386. X            got_dot = TRUE;
  387. X        *p2++ = *p;
  388. X        }
  389. X        if (!got_dot && isdigit(p[-1])) {
  390. X        int val;
  391. X        *p2 = 0; /* assure null termination */
  392. X        val = atoi(fmt+1);
  393. X        p2 += strlen(sprintf(p2, ".%d", (val >= 0 ? val : -val)));
  394. X        }
  395. X        *p2++ = 's', *p2 = 0;
  396. X        switch (*p) {
  397. X        case 'f': p2 = from;
  398. X        when 'a':
  399. X            if (!*(p2 = addr))
  400. X            p2 = from;
  401. X        when 'n':
  402. X            if (!*(p2 = name))
  403. X            p2 = from;
  404. X        when '%': p2 = "%";
  405. X        when 't': p2 = to;
  406. X        when 's': p2 = subject;
  407. X        when 'l': p2 = lines;
  408. X        when 'c': p2 = chars;
  409. X        /* date formatting chars */
  410. X        when 'd': p2 = date; /* the full date */
  411. X        when 'T': p2 = Tm;
  412. X        when 'M': p2 = Mon;
  413. X        when 'Y': p2 = Yr;
  414. X        when 'N': p2 = Day;
  415. X        when 'D': p2 = Wkday;
  416. X        otherwise: continue; /* unknown formatting char */
  417. X        }
  418. X        len = strlen(sprintf(b, fmt, p2));
  419. X        cnt += len, b += len;
  420. X        /* Get around a bug in 5.5 IBM RT which pads with NULL's not ' ' */
  421. X        while (cnt && !*(b-1))
  422. X        b--, cnt--;
  423. X    } else
  424. X        cnt++, *b++ = *p;
  425. X    for (*b-- = 0; isspace(*b); --b)
  426. X    *b = 0;
  427. X    return buf;
  428. X}
  429. X
  430. X/*
  431. X * Using message "n", build a list of recipients that you would mail to if
  432. X * you were to reply to this message.  If "all" is true, then it will take
  433. X * everyone from the To line in addition to the original sender.
  434. X * fix_address() is caled from mail.c, not from here.  There are too many
  435. X * other uses for reply_to to always require reconstruction of return paths.
  436. X * Note that we do NOT deal with Cc paths here either.
  437. X * Check to make sure that we in fact return a legit address (i.e. not blanks
  438. X * or null). If such a case occurs, return login name.  Always pad end w/blank.
  439. X */
  440. char *
  441. reply_to(n, all, buf)
  442. register char *buf;
  443. X{
  444. X    register char *p = NULL, *p2, *b = buf, *field;
  445. X    char line[256];
  446. X
  447. X    if (field = do_set(set_options, "reply_to_hdr")) {
  448. X#ifndef MSG_SEPARATOR
  449. X    if (!*field)
  450. X        goto From; /* special case -- get the colon-less From line */
  451. X#endif /* MSG_SEPARATOR */
  452. X    field = lcase_strcpy(line, field);
  453. X    while (*field) {
  454. X        if (p2 = any(field, " \t,:"))
  455. X        *p2 = 0;
  456. X        if ((p = header_field(n, field)) || !p2)
  457. X        break;
  458. X        else {
  459. X        field = p2+1;
  460. X        while (isspace(*field) || *field == ':' || *field == ',')
  461. X            field++;
  462. X        }
  463. X    }
  464. X    if (!p)
  465. X        print("Warning: message contains no `reply_to_hdr' headers.\n");
  466. X    }
  467. X    if (p || (!p && ((p = header_field(n, "from")) ||
  468. X        (p = header_field(n, "reply-to")) ||
  469. X        (p = header_field(n, "return-path")))))
  470. X    skipspaces(0);
  471. X    else if (!p) {
  472. X#ifndef MSG_SEPARATOR
  473. From:
  474. X    /* if all else fails, then get the first token in "From" line */
  475. X    if (fseek(tmpf, msg[n].m_offset, L_SET) == -1 ||
  476. X        !(p2 = fgets(line, sizeof(line), tmpf))) {
  477. X        error("fseek in %s (msg %d, folder=%s)", tempfile, n+1, mailfile);
  478. X        turnon(glob_flags, READ_ONLY);
  479. X        return "";
  480. X    }
  481. X    p = index(p2, ' ');
  482. X    skipspaces(1);
  483. X    if (p2 = index(p, ' '))
  484. X        *p2 = 0;
  485. X#else /* MSG_SEPARATOR */
  486. X    wprint("Warning: unable to find who msg %d is from!\n", n+1);
  487. X#endif /* MSG_SEPARATOR */
  488. X    }
  489. X    b += Strcpy(buf, p);
  490. X
  491. X    /*
  492. X     * if `all' is true, append everyone on the "To:" line.
  493. X     * cc_to(), called separately, will catch the cc's
  494. X     */
  495. X    if (all && (p = header_field(n, "to")) && *p) {
  496. X    b += Strcpy(b, ", ");
  497. X    /* The assumption that BUFSIZ is correct is unwise, but I know it
  498. X     * to be true for Mush.  Be forewarned if you call this routine.
  499. X     */
  500. X    (void) strncpy(b, p, BUFSIZ - (b - buf) - 2);
  501. X    buf[BUFSIZ-3] = 0;
  502. X    }
  503. X    fix_up_addr(buf);
  504. X    take_me_off(buf);
  505. X    for (p = buf; *p == ',' || isspace(*p); p++);
  506. X    if (!*p)
  507. X    (void) strcpy(buf, login);
  508. X    return strcat(buf, " ");
  509. X}
  510. X
  511. char *
  512. subject_to(n, buf)
  513. register char *buf;
  514. X{
  515. X    register char *p;
  516. X    buf[0] = 0; /* make sure it's already null terminated */
  517. X    if (!(p = header_field(n, "subject")))
  518. X    return NULL;
  519. X    if (strncmp(p, "Re:", 3))
  520. X    (void) strcpy(buf, "Re: ");
  521. X    return strcat(buf, p);
  522. X}
  523. X
  524. char *
  525. cc_to(n, buf)
  526. register char *buf;
  527. X{
  528. X    register char *p;
  529. X    buf[0] = 0; /* make sure it's already null terminated */
  530. X    if (!(p = header_field(n, "cc")))
  531. X    return NULL;
  532. X    fix_up_addr(buf);
  533. X    take_me_off(p);
  534. X    return strcpy(buf, p);
  535. X}
  536. X
  537. X/*
  538. X * fix addresses according to the sender's address.  If he's on a remote
  539. X * machine, chances are that the addresses of everyone else he mailed to
  540. X * are addresses from his machine.  Reconstruct those addresses to route
  541. X * thru the senders machine first.
  542. X */
  543. fix_addresses(to, cc)
  544. char *to, *cc;
  545. X{
  546. X    char pre_path[256], addr[256], name[256], buf[BUFSIZ], c, *p2;
  547. X    register char *next, *p, *b = buf, *str;
  548. X    int pre_len = 0;
  549. X
  550. X    pre_path[0] = 0;
  551. X    /* Get the address of the sender (which is always listed first) */
  552. X    if (!(next = get_name_n_addr(to, name, addr)))
  553. X    return;
  554. X
  555. X    /* fix up the sender's address; improve_uucp_path to optimize pre_path */
  556. X    improve_uucp_paths(addr);
  557. X
  558. X    /* if user didn't route via uucp, pre_path will be blank */
  559. X    if (p = rindex(addr, '!')) {
  560. X    c = *++p, *p = 0;
  561. X    (void) strcpy(pre_path, addr); /* the uucp route he used */
  562. X    pre_len = strlen(pre_path);
  563. X    *p = c;
  564. X    Debug("Routing thru \"%s\"\n", pre_path);
  565. X    }
  566. X
  567. X    b += Strcpy(b, addr);
  568. X    if (*name)
  569. X    b += strlen(sprintf(b, " (%s)", name));
  570. X    while (*next == ',' || isspace(*next)) /* move next to the next address */
  571. X    next++;
  572. X    if (*next) /* there's more to come on the To line */
  573. X    b += Strcpy(b, ", ");
  574. X    else {
  575. X    (void) strcpy(to, buf);
  576. X    if (!cc || !*cc)
  577. X        return;
  578. X    }
  579. X    for (str = next, c = 0; c < 2; str = cc, c++) {
  580. X    if (str == cc)
  581. X        b = buf;
  582. X    *b = 0; /* null terminate beginning in case there's nothing to do */
  583. X    if (!str || !*str)
  584. X        continue;
  585. X    do  {
  586. X        /* get_name returns a pointer to the next address */
  587. X        if (!(p = get_name_n_addr(str, name, addr)))
  588. X        break;
  589. X        /* check to see if there's enough buffer space to add this addr */
  590. X        if ((b - buf) + pre_len + strlen(addr) + strlen(name) + 5 >= BUFSIZ)
  591. X        break;
  592. X        while (p2 = index(addr, '@'))
  593. X        *p2++ = '%'; /* '@' has too high precedence for uucp paths */
  594. X        /* don't prepend the sender's path unless required */
  595. X        if (pre_len && strncmp(addr, pre_path, pre_len))
  596. X        b += Strcpy(b, pre_path);
  597. X        b += Strcpy(b, addr);
  598. X        if (*name)
  599. X        b += strlen(sprintf(b, " (%s)", name));
  600. X        while (*p == ',' || isspace(*p))
  601. X        p++;
  602. X        if (*p)
  603. X        b += Strcpy(b, ", ");
  604. X    } while (*(str = p));
  605. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  606. X        *b = 0;
  607. X    improve_uucp_paths(buf);
  608. X    if (c == 0)
  609. X        (void) strcpy(to, buf);
  610. X    else
  611. X        (void) strcpy(cc, buf);
  612. X    }
  613. X}
  614. X
  615. X/*
  616. X * pass a string describing header like, "Subject: ", current value, and
  617. X * whether or not to prompt for it or to just post the information.
  618. X * If do_prompt is true, "type in" the current value so user can either
  619. X * modify it, erase it, or add to it.
  620. X */
  621. char *
  622. set_header(str, curstr, do_prompt)
  623. register char *str, *curstr;
  624. X{
  625. X    static char       buf[BUFSIZ];
  626. X    int        offset = 0;
  627. X    register char  *p = curstr;
  628. X
  629. X    buf[0] = 0;
  630. X    wprint(str);
  631. X    fflush(stdout);         /* force str curstr */
  632. X    if (do_prompt) {
  633. X    if (curstr)
  634. X        for (p = curstr; *p; p++)
  635. X#ifdef SUNTOOL
  636. X        if (istool)
  637. X            rite(*p); /* mimics typing for the tool */
  638. X        else
  639. X#endif /* SUNTOOL */
  640. X        if (isoff(glob_flags, ECHO_FLAG))
  641. X            fputc((buf[offset++] = *p), stdout);
  642. X        else
  643. X#ifdef TIOCSTI
  644. X            if (ioctl(0, TIOCSTI, p) == -1) {
  645. X            error("ioctl: TIOCSTI");
  646. X            wprint("You must retype the entire line.\n%s", str);
  647. X            break;
  648. X            }
  649. X#else
  650. X                {
  651. X            wprint("WARNING: -e flag! Type the line over.\n%s", str);
  652. X            break;
  653. X        }
  654. X#endif /* TIOCSTI */
  655. X
  656. X    if (istool)
  657. X        return NULL;
  658. X    /* simulate the fact that we're getting input for the letter even tho
  659. X     * we may not be.  set_header is called before IS_GETTING is true,
  660. X     * but if we set it to true temporarily, then signals will return to
  661. X     * the right place (stop/continue).
  662. X     */
  663. X    {
  664. X        u_long getting = ison(glob_flags, IS_GETTING);
  665. X        if (!getting)
  666. X        turnon(glob_flags, IS_GETTING);
  667. X        if (Getstr(buf, sizeof(buf), offset) == -1)
  668. X        buf[0] = 0;
  669. X        if (!getting)
  670. X        turnoff(glob_flags, IS_GETTING);
  671. X    }
  672. X    } else
  673. X    puts(strcpy(buf, curstr));
  674. X    if (debug > 1)
  675. X    print("returning (%s) from set_header\n", buf);
  676. X    return buf;
  677. X}
  678. X
  679. X/*
  680. X * improve uucp paths by looking at the name of each host listed in the
  681. X * path given.
  682. X *    sun!island!pixar!island!argv
  683. X * It's a legal address, but redundant. Also, if we know we talk to particular
  684. X * hosts via uucp, then we can just start with that host and disregard the path
  685. X * preceding it.  So, first get the known hosts and save them. Then start
  686. X * at the end of the original path (at the last ! found), and move backwards
  687. X * saving each hostname.  If we get to a host that we know about, stop there
  688. X * and use that address.  If we get to a host we've already seen, then
  689. X * delete it and all the hosts since then until the first occurance of that
  690. X * hostname.  When we get to the beginning, the address will be complete.
  691. X *
  692. X * Return all results into the original buffer passed to us.  Since we can
  693. X * at worst not touch the path (shorten it if anything), we know we're not
  694. X * going to overrun the buffer.
  695. X */
  696. improve_uucp_paths(original)
  697. register char *original;
  698. X{
  699. X    char      *hostnames[128];
  700. X    char       name[BUFSIZ], addr[BUFSIZ], buf[BUFSIZ], *knowns, *end;
  701. X    register char *p, *recipient, *start = original, *b = buf;
  702. X    int           saved_hosts, i;
  703. X
  704. X    if (!original || !*original)
  705. X    return;
  706. X
  707. X    knowns = do_set(set_options, "known_hosts");
  708. X
  709. X    while (end = get_name_n_addr(start, name, addr)) {
  710. X    saved_hosts = 0;
  711. X    /* no uucp path, just user name [@host] with optional name attached */
  712. X    if (!(p = rindex(addr, '!'))) {
  713. X        char c = *end;
  714. X        *end = 0;
  715. X        b += Strcpy(b, start); /* copy the entire address with comments */
  716. X        *end = c;
  717. X        recipient = NULL;
  718. X    } else {
  719. X        recipient = p+1;
  720. X        while (p > addr) {
  721. X        /* null the '!' separating the rest of the path from the part
  722. X         * of the path preceding it and move p back to the previous
  723. X         * '!' (or beginning to addr) for hostname to point to.
  724. X         */
  725. X        for (*p-- = 0; p > addr && *p != '!'; p--)
  726. X            ;
  727. X        /* if p is not at the addr, move it forward past the '!' */
  728. X        if (p != addr)
  729. X            ++p; /* now points to a null terminated hostname */
  730. X#ifndef SYSV
  731. X        /* if host is ourselves, ignore this and preceding hosts */
  732. X        for (i = 0; i < MAX_HOST_NAMES && ourname[i]; i++)
  733. X            if (!lcase_strcmp(p, ourname[i]))
  734. X            break;
  735. X        if (i < MAX_HOST_NAMES && ourname[i])
  736. X            break; /* our own host is not included in the path */
  737. X#endif /* SYSV */
  738. X        /* check already saved hostnames. If host is one of them,
  739. X         * delete remaining hostnames since there is a redundant path.
  740. X         */
  741. X        for (i = 0; i < saved_hosts; i++)
  742. X            if (!lcase_strcmp(hostnames[i], p))
  743. X                saved_hosts = i;
  744. X
  745. X        hostnames[saved_hosts++] = p;
  746. X        /* If we know that we call this host, break */
  747. X        if (p == addr || knowns && chk_two_lists(p, knowns, " ,\t"))
  748. X            break;
  749. X        --p; /* move p back onto the '!'; it will not equal addr */
  750. X        }
  751. X        while (saved_hosts-- > 0) {
  752. X        b += Strcpy(b, hostnames[saved_hosts]);
  753. X        *b++ = '!';
  754. X        }
  755. X        if (recipient)
  756. X        b += Strcpy(b, recipient);
  757. X        if (*name)
  758. X        b += strlen(sprintf(b, " (%s)", name));
  759. X    }
  760. X    for (start = end; *start == ',' || isspace(*start); start++)
  761. X        ;
  762. X    if (!*start)
  763. X        break;
  764. X    b += Strcpy(b, ", ");
  765. X    }
  766. X    (void) strcpy(original, buf);
  767. X}
  768. X
  769. X/*
  770. X * rm_cmts_in_addr() removes the comment lines in addresses that result from
  771. X * sendmail or other mailers which append the user's "real name" on the
  772. X * from lines.  See get_name_n_addr().
  773. X */
  774. rm_cmts_in_addr(str)
  775. register char *str;
  776. X{
  777. X    char addr[BUFSIZ], buf[BUFSIZ], *start = str;
  778. X    register char *b = buf;
  779. X
  780. X    *b = 0;
  781. X    do  {
  782. X    if (!(str = get_name_n_addr(str, NULL, addr)))
  783. X        break;
  784. X    b += Strcpy(b, addr);
  785. X    while (*str == ',' || isspace(*str))
  786. X        str++;
  787. X    if (*str)
  788. X        b += Strcpy(b, ", ");
  789. X    } while (*str);
  790. X    for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  791. X    *b = 0;
  792. X    (void) strcpy(start, buf);
  793. X}
  794. X
  795. X/*
  796. X * take_me_off() is intended to search for the user's login name in an
  797. X * address string and remove it.  Note that string should be legal addresses
  798. X * only -- no (comments) allowed here.  See rm_cmts_in_addr().
  799. X * If "metoo" is set, don't touch addr. This implementation is very bug prone
  800. X * because the user's name may be a hostname or the path specified may
  801. X * be incomplete.
  802. X */
  803. take_me_off(str)
  804. char *str;
  805. X{
  806. X    int i = 0, rm_me;
  807. X    char addr[BUFSIZ], buf[BUFSIZ], *start = str;
  808. X    char *Alts;
  809. X    register char *p, *b = buf;
  810. X
  811. X    if (!str || !*str || do_set(set_options, "metoo"))
  812. X    return;
  813. X
  814. X    Alts = do_set(set_options, "alternates");
  815. X
  816. X    *b = 0;
  817. X    do  {
  818. X    if (!(p = get_name_n_addr(str, NULL, addr)))
  819. X        break;
  820. X    rm_me = FALSE;
  821. X    /* see if user's login is in the address */
  822. X    if (!strcmp(login, addr))
  823. X        rm_me = TRUE;
  824. X    /* if Alts is not set and the above strcmp failed, don't remove him */
  825. X    else if (*addr && Alts && *Alts && chk_two_lists(login,addr, "!@%=")) {
  826. X        /* To be in this block, there must be a remote address */
  827. X        i = 0; /* initialize 'i' in case while loop is skipped */
  828. X#ifndef SYSV
  829. X        /* see if the hostnames match our hostname. */
  830. X        while (i < MAX_HOST_NAMES && ourname[i])
  831. X        if (chk_two_lists(addr, ourname[i++], "!@%="))
  832. X            break;
  833. X#endif /* SYSV */
  834. X        /* If one of the hostnames in the address is one of user's
  835. X         * hostnames, remove this address. If the alternates
  836. X         * hostnames listed contains a hostname in the address, remove
  837. X         * from the list.
  838. X         */
  839. X        if (
  840. X#ifndef SYSV
  841. X        i < MAX_HOST_NAMES && ourname[i] ||
  842. X#endif /* SYSV */
  843. X        *Alts == '*' || !chk_two_lists(addr, Alts, "!@%= \t,"))
  844. X            rm_me = TRUE;
  845. X    }
  846. X    if (!rm_me) {
  847. X        char c = *p;
  848. X        *p = 0;
  849. X        b += Strcpy(b, str);
  850. X        *p = c;
  851. X    }
  852. X    while (*p == ',' || isspace(*p))
  853. X        p++;
  854. X    if (*p && !rm_me)
  855. X        b += Strcpy(b, ", ");
  856. X    } while (*(str = p));
  857. X    for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  858. X    *b = 0;
  859. X    (void) strcpy(start, buf);
  860. X}
  861. X
  862. X/*
  863. X * Place commas in between all addresses that don't already have
  864. X * them.  Addresses which use comments which are in parens or _not_
  865. X * within angle brackets *must* already have commas around them or
  866. X * you can't determine what is a comment and what is an address.
  867. X */
  868. fix_up_addr(str)
  869. register char *str;
  870. X{
  871. X    char buf[BUFSIZ], *start = str, c;
  872. X    register char *p, *b = buf;
  873. X
  874. X    *b = 0;
  875. X    do  {
  876. X    /* get_name returns a pointer to the next address */
  877. X    if (!(p = get_name_n_addr(str, NULL, NULL)))
  878. X        break;
  879. X    c = *p, *p = 0;
  880. X    if (strlen(str) + (b - buf) >= BUFSIZ - 2) {
  881. X        /* print("Address too long! Lost address: \"%s\"\n", str); */
  882. X        *p = c;
  883. X        break;
  884. X    }
  885. X    for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  886. X        *b = 0;
  887. X    for (*p = c; *p == ',' || isspace(*p); p++)
  888. X        ;
  889. X    if (*p)
  890. X        b += Strcpy(b, ", ");
  891. X    } while (*(str = p));
  892. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  893. X    *b = 0;
  894. X    (void) strcpy(start, buf);
  895. X}
  896. X
  897. X/*
  898. X * Get address and name from a string (str) which came from an address header
  899. X * in a message or typed by the user.  The string may contain one or more
  900. X * well-formed addresses.  Each must be separated by a comma.
  901. X *
  902. X * address, address, address
  903. X * address (comment or name here)
  904. X * comment or name <address>
  905. X * "Comment, even those with comma's!" <address>
  906. X * address (comma, (more parens), etc...)
  907. X *
  908. X * This does *not* handle cases like:
  909. X *    comment <address (comment)>
  910. X *
  911. X * find the *first* address here and return a pointer to the end of the
  912. X * address (usually a comma).  Return NULL on error: non-matching parens,
  913. X * brackets, quotes...
  914. X */
  915. char *
  916. get_name_n_addr(str, name, addr)
  917. register char *str, *name, *addr;
  918. X{
  919. X    register char *p, *p2, *beg_addr = addr, *beg_name = name, c;
  920. X
  921. X    if (addr)
  922. X    *addr = 0;
  923. X    if (name)
  924. X    *name = 0;
  925. X    if (!str || !*str)
  926. X    return NULL;
  927. X
  928. X    /* first check to see if there's something to look for */
  929. X    if (!(p = any(str, ",(<\""))) {
  930. X    /* no comma or indication of a quote character. Find a space and
  931. X     * return that.  If nothing, the entire string is a complete address
  932. X     */
  933. X    if (p = any(str, " \t"))
  934. X        c = *p, *p = 0;
  935. X    if (addr)
  936. X        (void) strcpy(addr, str);
  937. X    if (p)
  938. X        *p = c;
  939. X    return p? p : str + strlen(str);
  940. X    }
  941. X
  942. X    /* comma terminated before any comment stuff.  If so, check for whitespace
  943. X     * before-hand cuz it's possible that strings aren't comma separated yet
  944. X     * and they need to be.
  945. X     *
  946. X     * address address address, address
  947. X     *                        ^p  <- p points here.
  948. X     *        ^p2 <- should point here.
  949. X     */
  950. X    if (*p == ',') {
  951. X    c = *p, *p = 0;
  952. X    if (p2 = any(str, " \t"))
  953. X        *p = ',', c = *p2, p = p2;
  954. X    if (addr)
  955. X        (void) strcpy(addr, str);
  956. X    *p = c;
  957. X    return p;
  958. X    }
  959. X
  960. X    /* starting to get hairy -- we found an angle bracket. This means that
  961. X     * everything outside of those brackets are comments until we find that
  962. X     * all important comma.  A comment AFTER the <addr> :
  963. X     *  <address> John Doe
  964. X     * can't call this function recursively or it'll think that "John Doe"
  965. X     * is a string with two legal address on it (each name being an address).
  966. X     */
  967. X    if (*p == '<') { /* note that "str" stil points to comment stuff! */
  968. X    if (name && *str) {
  969. X        *p = 0;
  970. X        name += Strcpy(name, str);
  971. X        *p = '<';
  972. X    }
  973. X    if (!(p2 = index(p+1, '>'))) {
  974. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  975. X        return NULL;
  976. X    }
  977. X    if (addr) {
  978. X        /* to support <addr (comment)> style addresses, add code here */
  979. X        *p2 = 0;
  980. X        skipspaces(1);
  981. X        addr += Strcpy(addr, p);
  982. X        while (addr > beg_addr && isspace(*(addr-1)))
  983. X        *--addr = 0;
  984. X        *p2 = '>';
  985. X    }
  986. X    /* take care of the case "... <addr> com (ment)" */
  987. X    {
  988. X        int p_cnt = 0; /* parenthesis counter */
  989. X        p = p2;
  990. X        /* don't recurse yet -- scan till null, comma or '<'(add to name) */
  991. X        for (p = p2; p[1] && (p_cnt || p[1] != ',' && p[1] != '<'); p++) {
  992. X        if (p[1] == '(')
  993. X            p_cnt++;
  994. X        else if (p[1] == ')')
  995. X            p_cnt--;
  996. X        if (name)
  997. X            *name++ = p[1];
  998. X        }
  999. X        if (p_cnt) {
  1000. X        wprint("Warning! Malformed name: \"%s\"\n", name);
  1001. X        return NULL;
  1002. X        }
  1003. X    }
  1004. X    if (name && name > beg_name) {
  1005. X        while (isspace(*(name-1)))
  1006. X        --name;
  1007. X        *name = 0;
  1008. X    }
  1009. X    }
  1010. X
  1011. X    /* this is the worst -- now we have parentheses/quotes.  These guys can
  1012. X     * recurse pretty badly and contain commas within them.
  1013. X     */
  1014. X    if (*p == '(' || *p == '"') {
  1015. X    char *start = p;
  1016. X    int comment = 1;
  1017. X    c = *p;
  1018. X    /* "str" points to address while p points to comments */
  1019. X    if (addr && *str) {
  1020. X        *p = 0;
  1021. X        while (isspace(*str))
  1022. X        str++;
  1023. X        addr += Strcpy(addr, str);
  1024. X        while (addr > beg_addr && isspace(*(addr-1)))
  1025. X        *--addr = 0;
  1026. X        *p = c;
  1027. X    }
  1028. X    while (comment) {
  1029. X        if (c == '"' && !(p = index(p+1, '"')) ||
  1030. X        c == '(' && !(p = any(p+1, "()"))) {
  1031. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  1032. X        return NULL;
  1033. X        }
  1034. X        if (*p == '(') /* loop again on parenthesis. quote ends loop */
  1035. X        comment++;
  1036. X        else
  1037. X        comment--;
  1038. X    }
  1039. X    /* Something like ``Comment (Comment) <addr>''.  In this case
  1040. X     * the name should include both comment parts with the
  1041. X     * parenthesis.   We have to redo addr.
  1042. X     */
  1043. X    if ((p2 = any(p+1, "<,")) && *p2 == '<') {
  1044. X        if (!(p = index(p2, '>'))) {
  1045. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  1046. X        return NULL;
  1047. X        }
  1048. X        if (addr = beg_addr) { /* reassign addr and compare to null */
  1049. X        c = *p; *p = 0;
  1050. X        addr += Strcpy(addr, p2+1);
  1051. X        while (addr > beg_addr && isspace(*(addr-1)))
  1052. X            *--addr = 0;
  1053. X        *p = c;
  1054. X        }
  1055. X        if (name) {
  1056. X        c = *p2; *p2 = 0;
  1057. X        name += Strcpy(name, str);
  1058. X        while (name > beg_name && isspace(*(name-1)))
  1059. X            *--name = 0;
  1060. X        *p2 = c;
  1061. X        }
  1062. X    } else if (name && start[1]) {
  1063. X        c = *p, *p = 0; /* c may be ')' instead of '(' now */
  1064. X        name += Strcpy(name, start+1);
  1065. X        while (name > beg_name && isspace(*(name-1)))
  1066. X        *--name = 0;
  1067. X        *p = c;
  1068. X    }
  1069. X    }
  1070. X    skipspaces(1);
  1071. X    /* this is so common, save time by returning now */
  1072. X    if (!*p || *p == ',')
  1073. X    return p;
  1074. X    return get_name_n_addr(p, name, addr);
  1075. X}
  1076. END_OF_FILE
  1077. if test 30675 -ne `wc -c <'hdrs.c'`; then
  1078.     echo shar: \"'hdrs.c'\" unpacked with wrong size!
  1079. fi
  1080. # end of 'hdrs.c'
  1081. fi
  1082. echo shar: End of archive 10 \(of 14\).
  1083. cp /dev/null ark10isdone
  1084. MISSING=""
  1085. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
  1086.     if test ! -f ark${I}isdone ; then
  1087.     MISSING="${MISSING} ${I}"
  1088.     fi
  1089. done
  1090. if test "${MISSING}" = "" ; then
  1091.     echo You have unpacked all 14 archives.
  1092.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1093. else
  1094.     echo You still need to unpack the following archives:
  1095.     echo "        " ${MISSING}
  1096. fi
  1097. ##  End of shell archive.
  1098. exit 0
  1099.